unit fGlsViewer;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  Winapi.OpenGL,
  System.SysUtils,
  System.Classes,
  System.Math,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  Vcl.StdCtrls,
  Vcl.ComCtrls,
  Vcl.Menus,
  Vcl.ExtCtrls,
  ShellApi,
  Vcl.Imaging.Jpeg,

  uConstants,
  uGlsVector,
  uGlsTree,
  uModelParameters,
  GLS.VectorGeometry,
  GLS.Context,
  GLS.Scene,
  GLS.Objects,
  GLS.Material,

  GLS.FileJPEG,
  GLS.SceneViewer,
  GLS.VectorFileObjects,
  GLS.Texture,
  GLS.Cadencer,
  // AVIRerun,
  GLS.AVIRecorder,
  GLS.Coordinates,
  GLS.BaseClasses,
  GLS.Utils;

type
  TFormGlsViewer = class(TForm)
    Panel1: TPanel;
    MainMenu1: TMainMenu;
    File1: TMenuItem;
    Open1: TMenuItem;
    Save1: TMenuItem;
    ProgressBar: TProgressBar;
    CancelButton: TButton;
    N1: TMenuItem;
    Exit1: TMenuItem;
    Help1: TMenuItem;
    GLScene1: TGLScene;
    GLSceneViewer1: TGLSceneViewer;
    GLCamera: TGLCamera;
    GLLightSource1: TGLLightSource;
    GLDummyCube1: TGLDummyCube;
    Help2: TMenuItem;
    About1: TMenuItem;
    TreeFreeForm: TGLFreeForm;
    Action1: TMenuItem;
    ZoomIn1: TMenuItem;
    ZoomOut1: TMenuItem;
    Cancel1: TMenuItem;
    N2: TMenuItem;
    DistanceCB: TCheckBox;
    OpenTdfDialog: TOpenDialog;
    GLMaterialLibrary: TGLMaterialLibrary;
    FruitFF: TGLFreeForm;
    SaveDialog1: TSaveDialog;
    QualityTrackBar: TTrackBar;
    Label133: TLabel;
    Label131: TLabel;
    QualityLabel: TLabel;
    Label132: TLabel;
    VectorLabel: TLabel;
    LeavesLabel: TLabel;
    StemsLabel: TLabel;
    LeavesVLabel: TLabel;
    WindStrengthTB: TTrackBar;
    Label37: TLabel;
    WindStrengthLabel: TLabel;
    WindGustTB: TTrackBar;
    Label136: TLabel;
    WindGustLabel: TLabel;
    WindFromTB: TTrackBar;
    Label43: TLabel;
    WindFromLabel: TLabel;
    WindAnimationCB: TCheckBox;
    AnimationCB: TCheckBox;
    GLCadencer1: TGLCadencer;
    N3: TMenuItem;
    RendertoBitmap1: TMenuItem;
    Snapshot1: TMenuItem;
    N4: TMenuItem;
    Bitmap1: TMenuItem;
    Bitmapx21: TMenuItem;
    Bitmap300dpi1: TMenuItem;
    Bitmap600dpi1: TMenuItem;
    RendertoAVI1: TMenuItem;
    Rotate3601: TMenuItem;
    SkyRanger1: TMenuItem;
    WindPilot1: TMenuItem;
    FruitVLabel: TLabel;
    FruitLabel: TLabel;
    LeafFF: TGLFreeForm;
    GLLightSource2: TGLLightSource;
    N5: TMenuItem;
    DisplayControls1: TMenuItem;
    SelectBackgroundColor1: TMenuItem;
    ColorDialog1: TColorDialog;
    CubeAxisVisible1: TMenuItem;
    GLAVIRecorder1: TGLAVIRecorder;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);

    procedure Exit1Click(Sender: TObject);
    procedure Help1Click(Sender: TObject);
    procedure CancelButtonClick(Sender: TObject);

    procedure Save1Click(Sender: TObject);

    procedure GLSceneViewer1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure GLSceneViewer1MouseMove(Sender: TObject; Shift: TShiftState;
      X, Y: Integer);
    procedure GLSceneViewer1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
    procedure ZoomIn1Click(Sender: TObject);
    procedure ZoomOut1Click(Sender: TObject);
    procedure ZoomCentered;

    procedure Open1Click(Sender: TObject);
    procedure LoadParameters;

    procedure GLCadencer1Progress(Sender: TObject;
      const deltaTime, newTime: Double);
    procedure AnimationCBClick(Sender: TObject);
    procedure WindAnimationCBClick(Sender: TObject);
    procedure WindStrengthTBChange(Sender: TObject);
    procedure WindGustTBChange(Sender: TObject);
    procedure WindFromTBChange(Sender: TObject);

    procedure DistanceCBClick(Sender: TObject);
    procedure QualityTrackBarChange(Sender: TObject);

    procedure Snapshot1Click(Sender: TObject);
    procedure Bitmap1Click(Sender: TObject);
    procedure Bitmapx21Click(Sender: TObject);
    procedure Bitmap300dpi1Click(Sender: TObject);
    procedure Bitmap600dpi1Click(Sender: TObject);

    procedure Rotate3601Click(Sender: TObject);
    procedure SkyRanger1Click(Sender: TObject);
    procedure WindPilot1Click(Sender: TObject);

    procedure MakeAVIBmp;
    procedure DisplayControls1Click(Sender: TObject);
    procedure SelectBackgroundColor1Click(Sender: TObject);
    procedure CubeAxisVisible1Click(Sender: TObject);

  private

    mx, my: Integer; { Mouse }
    md: Boolean;
    AnimeChannel: Integer;
    FTree: TTree;
    FModelParameters: TModelParameters;
    TotalFruit, TotalFruitVertices, ViewerTotalLeaves,
      TotalLeaveVertices: Integer;
    TotalStems: Integer;
    TotalVertices: Integer;
    TotalFaces: Integer;
    function Initialize(MaxX, MaxY, MaxZ: real; ATotalStems: Integer): Integer;
    function AddStem(Sections: TList): Integer;
    function AddLeaf(Points: TList; LeafColor: Integer;
      LeafColorVariation: Boolean): Integer;
    function AddFruit(Points: TList; FruitColor: Integer;
      FruitColorVariation: Boolean): Integer;

    procedure RenderToBitmap(scale: Single);
    procedure AnimeMotion;
    procedure ComputeAnimeMotion;
  public

  end;

var
  FormGlsViewer: TFormGlsViewer;

implementation

uses
  fGlsViewBitmap,
  GLS.Graphics,
  GLS.Keyboard,
  fGlsTree;

{$R *.DFM}

procedure TFormGlsViewer.FormCreate(Sender: TObject);
begin
  TreePlanted := False;
  AnimationActive := False;
  DistanceActive := False;
  WindAnimationActive := False;
  GlsViewerCancelled := False;
  AnimeChannel := 0;
end;

procedure TFormGlsViewer.Exit1Click(Sender: TObject);
begin
  Close;
end;

procedure TFormGlsViewer.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  GLCadencer1.Enabled := False;
  GlsViewerCancelled := True;
  Application.ProcessMessages;
  If TreePlanted then
  begin
    FTree.Destroy; { Tree data } { Freeform data }
  end;
  TreePlanted := False;
  AnimationActive := False;
  DistanceActive := False;
  WindAnimationActive := False;
end;

procedure TFormGlsViewer.Help1Click(Sender: TObject);
begin
  ShellExecute(Application.Handle, // handle to parent window
    'open', // pointer to string that specifies operation to perform
    PChar(ExtractFilePath(ParamStr(0)) + 'help\glsview.htm'),
    { PChar(POVRayExecutable), }// pointer to filename or folder name string
    '' { PChar(POVRayCommandLine) } ,
    // pointer to string that specifies executable-file parameters
    PChar(ExtractFilePath(ParamStr(0)) + 'help'),
    // pointer to string that specifies default directory
    SW_SHOWNORMAL);
end;

procedure TFormGlsViewer.CubeAxisVisible1Click(Sender: TObject);
begin
  CubeAxisVisible1.Checked := (not CubeAxisVisible1.Checked);
  GLDummyCube1.VisibleAtRunTime := CubeAxisVisible1.Checked;
  GLDummyCube1.ShowAxes := CubeAxisVisible1.Checked;
end;

procedure TFormGlsViewer.DisplayControls1Click(Sender: TObject);
begin
  DisplayControls1.Checked := (not DisplayControls1.Checked);
  If DisplayControls1.Checked then
  begin
    GLSceneViewer1.Align := alnone;
    Panel1.Visible := True;
    Panel1.Align := alBottom;
  end
  else
  begin
    GLSceneViewer1.Align := alnone;
    Panel1.Visible := False;
    Panel1.Align := alnone;
  end;
  GLSceneViewer1.Align := alClient;
end;

procedure TFormGlsViewer.GLSceneViewer1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  mx := X;
  my := Y;
  md := True;
end;

procedure TFormGlsViewer.GLSceneViewer1MouseMove(Sender: TObject;
  Shift: TShiftState; X, Y: Integer);
var
  d: Single;
begin
  if md and (ssLeft in Shift) then
  begin
    { if ssLeft in Shift then begin }

    if ssCtrl in Shift then
    begin
      d := GLCamera.DistanceToTarget * 0.01 * (X - mx + Y - my);
      if IsKeyDown('x') then
        TreeFreeForm.Translate(d, 0, 0)
      else if IsKeyDown('y') then
        TreeFreeForm.Translate(0, d, 0)
      else if IsKeyDown('z') then
        TreeFreeForm.Translate(0, 0, d)
      else
        GLCamera.RotateObject(TreeFreeForm, my - Y, mx - X);
    end
    else
      GLCamera.MoveAroundTarget(my - Y, mx - X);
    mx := X;
    my := Y;
  end;
end;

procedure TFormGlsViewer.GLSceneViewer1MouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  md := False;
end;

procedure TFormGlsViewer.FormMouseWheel(Sender: TObject; Shift: TShiftState;
  WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
  if TreeFreeForm.MeshObjects.Count > 0 then
  begin
    GLCamera.AdjustDistanceToTarget(Power(1.05, WheelDelta / 120));
    GLCamera.DepthOfView := 2 * GLCamera.DistanceToTarget + 2 *
      TreeFreeForm.BoundingSphereRadius;
  end;
  Handled := True;
end;

procedure TFormGlsViewer.ZoomIn1Click(Sender: TObject);
var
  h: Boolean;
begin
  FormMouseWheel(Self, [], -120 * 4, Point(0, 0), h);
end;

procedure TFormGlsViewer.ZoomOut1Click(Sender: TObject);
var
  h: Boolean;
begin
  FormMouseWheel(Self, [], 120 * 4, Point(0, 0), h);
end;

procedure TFormGlsViewer.ZoomCentered;
var
  h: Boolean;
begin
  FormMouseWheel(Self, [], 120, Point(0, 0), h);
end;

{ A utility function, this takes the bitmap and uses Form2 to display it with
  a regular TImage component. }
procedure ViewBitmap(aBitmap: TBitmap; caption: String);
begin
  ViewBitmapForm := TViewBitmapForm.Create(Application);
  ViewBitmapForm.Image1.Picture.Bitmap := aBitmap;
  ViewBitmapForm.Image1.Width := aBitmap.Width;
  ViewBitmapForm.Image1.Height := aBitmap.Height;
  ViewBitmapForm.caption := caption;
  if (aBitmap.Width < Screen.Width) and (aBitmap.Height < Screen.Height) then
  begin
    ViewBitmapForm.Width := aBitmap.Width;
    ViewBitmapForm.Height := aBitmap.Height;
  end
  else
  begin
    ViewBitmapForm.Width := Round(Screen.Width * 0.75);
    ViewBitmapForm.Height := Round(Screen.Height * 0.75);
  end;
  try
    ViewBitmapForm.ShowModal;
  finally
    ViewBitmapForm.Release;
  end;
end;

procedure TFormGlsViewer.SelectBackgroundColor1Click(Sender: TObject);
begin
  ColorDialog1.Color := GLSceneViewer1.Buffer.BackGroundColor;
  If ColorDialog1.Execute then
    GLSceneViewer1.Buffer.BackGroundColor := ColorDialog1.Color;
  { GLSceneViewer1.Buffer.BackGroundColor:=clBtnFace; }
end;

procedure TFormGlsViewer.RenderToBitmap(scale: Single);
var
  bmp: TBitmap;
  pt: Int64;
  delta: Double;
begin
  pt := StartPrecisionTimer;
  // Rendering to a bitmap requires an existing bitmap,
  // so we create and size a new one
  bmp := TBitmap.Create;
  // Don't forget to specify a PixelFormat, or current screen pixel format
  // will be used, which may not suit your purposes!
  bmp.PixelFormat := pf24bit;
  bmp.Width := Round(GLSceneViewer1.Width * scale);
  bmp.Height := Round(GLSceneViewer1.Height * scale);
  // Here we just request a render
  // The second parameter specifies DPI (Dots Per Inch), which is
  // linked to the bitmap's scaling
  // "96" is the "magic" DPI scale of the screen under windows
  GLSceneViewer1.Buffer.RenderToBitmap(bmp, Round(96 * scale));
  delta := StopPrecisionTimer(pt);
  ViewBitmap(bmp, Format('RenderToBitmap %dx%d - %.1f ms',
    [bmp.Width, bmp.Height, delta * 1000]));
  bmp.Free;
end;

procedure TFormGlsViewer.Snapshot1Click(Sender: TObject);
var
  bmp32: TGLBitmap32;
  bmp: TBitmap;
  pt: Int64;
  delta: Double;
begin
  pt := StartPrecisionTimer;
  // CreateSnapShot returns a TGLBitmap32, which is a low-level data buffer.
  // However TGLBitmap32 can spawn a regular TBitmap, which we use here
  bmp32 := GLSceneViewer1.Buffer.CreateSnapShot;
  bmp := bmp32.Create32BitsBitmap;
  delta := StopPrecisionTimer(pt);
  // Display the bitmap for the user to see and gaze in everlasting awe...
  ViewBitmap(bmp, Format('SnapShot %dx%d - %.3f ms', [bmp.Width, bmp.Height,
    delta * 1000]));
  // Don't forget to free your TGLBitmap32 and TBitmap!
  bmp.Free;
  bmp32.Free;
end;

procedure TFormGlsViewer.Bitmap1Click(Sender: TObject);
begin
  // Render at viewer resolution (scale = 1, DPI = 96)
  RenderToBitmap(1);
end;

procedure TFormGlsViewer.Bitmapx21Click(Sender: TObject);
begin
  // Render at twice viewer resolution (scale = 2, DPI = 192 = 96x2)
  RenderToBitmap(2);
end;

procedure TFormGlsViewer.Bitmap300dpi1Click(Sender: TObject);
begin
  // Screen is "magic" 96 dpi, this gives us our scale
  RenderToBitmap(300 / 96);
end;

procedure TFormGlsViewer.Bitmap600dpi1Click(Sender: TObject);
begin
  // Screen is "magic" 96 dpi, this gives us our scale
  RenderToBitmap(600 / 96);
end;

procedure TFormGlsViewer.GLCadencer1Progress(Sender: TObject;
  const deltaTime, newTime: Double);
begin
  { GLCadencer1.Enabled:= AnimationCB.Checked;
    DistanceActive
    QualityTrackBar.Position
    WindAnimationActive
    WindStrengthTB.Position
    WindGustTB.Position
    WindFromTB.Position }
  ComputeAnimeMotion;
end;

procedure TFormGlsViewer.AnimationCBClick(Sender: TObject);
begin
  GLCadencer1.Enabled := AnimationCB.Checked;
  AnimationActive := AnimationCB.Checked;
  If AnimationCB.Checked then
  begin
    { Done in Cadencer }
  end
  else
  begin { Turn them all OFF }
    DistanceCB.Checked := AnimationCB.Checked;
    DistanceActive := DistanceCB.Checked;
    WindAnimationCB.Checked := AnimationCB.Checked;
    WindAnimationActive := WindAnimationCB.Checked;
  end;
end;

procedure TFormGlsViewer.WindAnimationCBClick(Sender: TObject);
begin
  WindAnimationActive := WindAnimationCB.Checked;
end;

procedure TFormGlsViewer.WindStrengthTBChange(Sender: TObject);
begin
  WindStrengthLabel.caption := Inttostr(WindStrengthTB.Position);
end;

procedure TFormGlsViewer.WindGustTBChange(Sender: TObject);
begin
  WindGustLabel.caption := Inttostr(WindGustTB.Position);
end;

procedure TFormGlsViewer.WindFromTBChange(Sender: TObject);
begin
  WindFromLabel.caption := Inttostr(WindFromTB.Position);
end;

procedure TFormGlsViewer.DistanceCBClick(Sender: TObject);
begin
  DistanceActive := DistanceCB.Checked;
end;

procedure TFormGlsViewer.QualityTrackBarChange(Sender: TObject);
begin
  QualityLabel.caption := Inttostr(QualityTrackBar.Position);
  If ((not DistanceActive) and AnimationActive) then
    FModelParameters.Distance := QualityTrackBar.Position;
  { Cadencer does what it can }
end;

procedure TFormGlsViewer.Rotate3601Click(Sender: TObject);
begin { Record AVI while rotating Cube-Tree }
  AnimeChannel := 1;
  AnimationCB.Checked := False;
  GLCadencer1.Enabled := False;
  MakeAVIBmp;
end;

procedure TFormGlsViewer.SkyRanger1Click(Sender: TObject);
begin
  { Record AVI while Range Degrading Cube-Tree }
  AnimeChannel := 2;
  AnimationCB.Checked := False;
  GLCadencer1.Enabled := False;
  MakeAVIBmp;
end;

// Record AVI while Blowing Wind up the Cube-Tree
procedure TFormGlsViewer.WindPilot1Click(Sender: TObject);
begin
  AnimeChannel := 3;
  AnimationCB.Checked := False;
  GLCadencer1.Enabled := False;
  MakeAVIBmp;
end;

procedure TFormGlsViewer.AnimeMotion;
Begin
  GLCadencer1.Enabled := False; { just in case }
  Case AnimeChannel of
    1: { rotating Cube-Tree }
      Begin
        { Rotate per Anime360Counter
          GLCamera.MoveAroundTarget(my-y, mx-x);
          TreeFreeForm.Translate(0, d, 0)
          GLCamera.RotateObject(TreeFreeForm, my-y, mx-x); }
      End;
    2: { Range Degrading Cube-Tree }
      Begin { Static View... Change the Tree degradation }
        FModelParameters.Distance := ProgressBar.Position;
        { d:=GLCamera.DistanceToTarget*0.01*(x-mx+y-my); }
        { Accounts for Mouse distance changes ? }
        { GLCamera.DistanceToTarget }
        { DistanceActive        QualityTrackBar.Position }
        { DO the Range MATH and LOD changes }

      End;
    3: { Blowing Wind up the Cube-Tree }
      Begin { Do with Anime360Counter changing ______ }
        { WindAnimationActive
          WindStrengthTB.Position
          WindGustTB.Position
          WindFromTB.Position }
      End;
  end;
  { Compute NEW TREE }
  ComputeAnimeMotion;
End;

procedure TFormGlsViewer.ComputeAnimeMotion;
Begin
  LoadParameters;
End;

procedure TFormGlsViewer.MakeAVIBmp;
var
  bmp32 : TGLBitmap32;
  bmp : TBitmap;
  I:Integer;
  SavedCap: string;
begin
  (* AVIRerun1.Compressor:=acShowDialog;
    AVIRerun1.FileName:=  ChangeFileExt(aGlsTreeForm.TreeDefFileNameEdit.Text,'.avi');
    AVIRerun1.FPS:=24;
    AVIRerun1.Height:=GLSceneViewer1.Height;
    AVIRerun1.Width:=GLSceneViewer1.Width; *)
  // if AVIRecorder1.filename is empty, a dialog box will appear asking
  // for the filename. CreateAVIFile() will return a bool
  // indicating if user presses "cancel" in the dialog box.
  if not GLAVIRecorder1.CreateAVIFile then
    Exit;
  SavedCap := FormGlsViewer.caption;
  GlsViewerCancelled := False;
  FormGlsViewer.caption := 'Press Cancel Button to abort';
  ProgressBar.Position := 0;
  Application.ProcessMessages;
  Anime360Counter := 0;

  try
    while (Anime360Counter < 360) and not GlsViewerCancelled do
    begin
      // Create the Changed Tree
      AnimeMotion;
      ProgressBar.Position := Round((Anime360Counter / 360) * 100);
      // you might want to update your progress bar here.
      Application.ProcessMessages; // so that our app. is not freezed,
      // and will accept user abort.
      GLAVIRecorder1.AddAVIFrame;
      inc(Anime360Counter);
    end;

    (*
      if (not AVIRerun1.CreateAVIFile(GlsViewerForm.Handle)) then Exit;
      Application.ProcessMessages;
      {Process Bitmaps into AVI}
      {Create the FIRST .. INITIALIZED Changed Tree}
      bmp32:=GLSceneViewer1.Buffer.CreateSnapShot;
      bmp:=bmp32.Create32BitsBitmap;
      AVIRerun1.AddAVIFrame(bmp);
      i:=1;
      while ( (i< (360)) and (not GlsViewerCancelled)) do
      Begin
      ProgressBar.Position:=round((i/360)*100);
      // you might want to update your progress bar here.
      Application.ProcessMessages; // so that our app. is not freezed,
      // and will accept user abort.
      {Create the Changed Tree}

      bmp32:=GLSceneViewer1.Buffer.CreateSnapShot;
      bmp:=bmp32.Create32BitsBitmap;
      AVIRerun1.AddAVIFrame(bmp);
      inc(i);
      End;{While}
    *)
  finally
    // if GlsViewerCancelled, CloseAVIFile will
    // also delete the unfinished file.
    // Don't forget to free your TGLBitmap32 and TBitmap!
    // AVIRerun1.CloseAVIFile(GlsViewerCancelled);
    // bmp.Free;
    // bmp32.Free;
    GLAVIRecorder1.CloseAVIFile(GlsViewerCancelled);
    // if UserAbort, CloseAVIFile will
    // also delete the unfinished file.
    FormGlsViewer.caption := SavedCap;
    ProgressBar.Position := 0;
  end;
End;

procedure TFormGlsViewer.Save1Click(Sender: TObject);
begin { Save as .obj  .ms3d  ?
    NONE WORK, OBJ only 1 mesh, others : NO SAVE FILE done yet }
  SaveDialog1.Filter := '*.obj';
  SaveDialog1.Filename := ChangeFileExt(OpenTdfDialog.Filename, '.obj');
  SaveDialog1.InitialDir := GlsDefinitionPath;
  if SaveDialog1.Execute then
  begin
    Application.ProcessMessages;
    GlsDefinitionPath := ExtractFilePath(SaveDialog1.Filename);
    { ChangeFileExt(SaveDialog1.FileName,'.obj') }
    TreeFreeForm.SaveToFile(SaveDialog1.Filename);
  end;
end;

// Open a tree definition file  .tdf
procedure TFormGlsViewer.Open1Click(Sender: TObject);
begin
  OpenTdfDialog.InitialDir := GlsDefinitionPath;
  if OpenTdfDialog.Execute then
  begin
    Application.ProcessMessages;
    GlsDefinitionPath := ExtractFilePath(OpenTdfDialog.Filename);
    aGlsTreeForm.LoadTreeDefinition(OpenTdfDialog.Filename);
    LoadParameters;
  end;
end;

procedure TFormGlsViewer.CancelButtonClick(Sender: TObject);
begin
  { if MessageDlg('Are you sure?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then }
  GlsViewerCancelled := True;
end;

procedure TFormGlsViewer.LoadParameters;
var
  iii: Integer;
  function ExportTreeMesh(AStem: TStem): Integer;
  { Pre: AStem <> nil
    Ret: 0, ExportTreeMesh was not successful
    1, ExportTreeMesh was successful, the tree mesh was successfully
    processed by the selected plugin }
  var
    i: Integer;
  begin
    with AStem do
    begin
      Application.ProcessMessages;
      if AddStem(Sections) = 0 then
      begin
        Result := 0;
        Exit;
      end;
      Result := 1;
      for i := 0 to SubStems.Count - 1 do
      begin
        if ExportTreeMesh(TStem(SubStems[i])) = 0 then
        begin
          Result := 0;
          Exit;
        end;
        Result := 1;
      end;
      for i := 0 to Fruit.Count - 1 do
      begin
        if AddFruit(Fruit[i], FModelParameters.FruitColor,
          FModelParameters.FruitColorVariation) = 0 then
        begin
          Result := 0;
          Exit;
        end;
        Result := 1;
      end;

      for i := 0 to Leafs.Count - 1 do
      begin
        if AddLeaf(Leafs[i], FModelParameters.LeafColor,
          FModelParameters.LeafColorVariation) = 0 then
        begin
          Result := 0;
          Exit;
        end;
        Result := 1;
      end;

    end;
  end;

begin
  If TreePlanted then { Destroy stuff so NOT doubled when working per Level }
  begin
    FTree.Destroy; { Tree data }
    { Freeform data }
    { for iii:=TreeFreeForm.MeshObjects.Count-1 downto 0 do
      TreeFreeForm.MeshObjects.free;
      for iii:=LeafFF.MeshObjects.Count-1 downto 0 do
      LeafFF.MeshObjects.free;
      for iii:=FruitFF.MeshObjects.Count-1 downto 0 do
      FruitFF.MeshObjects.free; }
  end;
  { Initialize the model parameters class }
  with aGlsTreeForm do
  begin
    LevelsNumberEditExit(nil);
    { The last level of recursion is reserved for leaves, so we only create
      LevelsNumberEdit.GetValue - 1 stem parameter levels. }
    FModelParameters := TModelParameters.Create(GlobalLevelsNumber);
    if GlobalLevelsNumber = 0 then
    begin
      MessageDlg('Levels should be a value greater than zero!', mtError,
        [mbOK], 0);
      Exit;
    end;
    InitModelParameters(FModelParameters);
    { Initialize the random number generator }
    if RandomTreeSeedNumber = 0 then
      Randomize
    else
      RandSeed := RandomTreeSeedNumber;
  end;
  if Random(2) = 0 then
    WindSign := -1
  else
    WindSign := 1;
  QualityTrackBar.Position := Round(FModelParameters.Distance);

  // load the materials
  If FileExists(FModelParameters.TreeBarkFile) then
  Begin
    { GLMaterialLibrary.AddTextureMaterial('TreeBark', FModelParameters.TreeBarkFile) }
    GLMaterialLibrary.Materials[0].Material.Texture.Image.LoadFromFile
      (FModelParameters.TreeBarkFile);
    TreeFreeForm.Material.MaterialLibrary := GLMaterialLibrary;
    TreeFreeForm.Material.LibMaterialName := 'TreeBark';
  end
  else
  begin
    TreeFreeForm.Material.LibMaterialName := '';
    FModelParameters.TreeBarkFile := '';
  end;

  If FileExists(FModelParameters.LeafTextureFile) then
  Begin
    { GLMaterialLibrary.AddTextureMaterial('LeafTexture', FModelParameters.LeafTextureFile) }
    GLMaterialLibrary.Materials[1].Material.Texture.Image.LoadFromFile
      (FModelParameters.LeafTextureFile);
    LeafFF.Material.MaterialLibrary := GLMaterialLibrary;
    LeafFF.Material.LibMaterialName := 'LeafTexture';
  end
  else
  begin
    LeafFF.Material.LibMaterialName := '';
    FModelParameters.LeafTextureFile := '';
  end;

  If FileExists(FModelParameters.FruitTextureFile) then
  Begin
    { GLMaterialLibrary.AddTextureMaterial('LeafTexture', FModelParameters.LeafTextureFile) }
    GLMaterialLibrary.Materials[2].Material.Texture.Image.LoadFromFile
      (FModelParameters.FruitTextureFile);
    FruitFF.Material.MaterialLibrary := GLMaterialLibrary;
    FruitFF.Material.LibMaterialName := 'FruitTexture';
  end
  else
  begin
    FruitFF.Material.LibMaterialName := '';
    FModelParameters.FruitTextureFile := '';
  end;

  { Set colors.. does not work... }
  TreeFreeForm.Material.FrontProperties.Emission.AsWinColor :=
    FModelParameters.TreeColor;
  FruitFF.Material.BackProperties.Emission.AsWinColor :=
    FModelParameters.FruitColor;
  LeafFF.Material.BackProperties.Emission.AsWinColor :=
    FModelParameters.LeafBackColor;

  GlsViewerCancelled := False;
  { Create the entire tree structure }
  FTree := TTree.Create(FModelParameters, FModelParameters.Season
    { aGlsTreeForm.SeasonComboBox.ItemIndex } );
  FTree.Trunk.CreateStructure(nil, FTree.TrunkLength, 0, 0);
  ProgressBar.Max := FTree.TotalStems + TreeAddedLeavesTotal +
    FTree.TotalGrownLeaves + FTree.TotalFruit - 1;
  ProgressBar.Position := 0;

  { And create the tree mesh points }
  TreePlanted := True; { This tells form to delete
    AND signals Progress bar use in GLSTree }
  FTree.Grow;

  { export the tree structure to a 3d format }
  if not GlsViewerCancelled then
  begin
    { Call the initialization section }
    if Initialize(FTree.MaxX, FTree.MaxY, FTree.MaxZ, FTree.TotalStems) = 0 then
      Exit
    else
    begin
      { Export the mesh structure to the FreeForm }
      if ExportTreeMesh(FTree.Trunk) = 0 then
      begin
        MessageDlg('Error computing tree mesh!', mtError, [mbOK], 0);
        Exit;
      end;
    end;
    { Free the memory occupied by the internal representation of the tree
      When the form is Closed  TreePlanted:=True; }

    { Freeform data changed }
    ProgressBar.Position := 0;
    TreeFreeForm.StructureChanged;
    FruitFF.StructureChanged;
    LeafFF.StructureChanged;
    ZoomCentered;
    StemsLabel.caption := Inttostr(TotalStems);
    VectorLabel.caption := Inttostr(TotalVertices);
    LeavesLabel.caption :=
      Inttostr( { TreeAddedLeavesTotal+ } ViewerTotalLeaves);
    LeavesVLabel.caption := Inttostr(TotalLeaveVertices);
    FruitLabel.caption := Inttostr(TotalFruit);
    FruitVLabel.caption := Inttostr(TotalFruitVertices);
  end;
end;

function TFormGlsViewer.Initialize(MaxX, MaxY, MaxZ: real;
  ATotalStems: Integer): Integer;
begin
  ProgressBar.Max := ATotalStems + TreeAddedLeavesTotal + FTree.TotalGrownLeaves
    + FTree.TotalFruit - 1;
  { Need to CLEAR all the Trunk and Leaf Objects... }

  { TotalVertices and TotalFaces hold the total amount of vertices and faces
    in the entire tree }
  TotalVertices := 0;
  TotalFaces := 0;
  Bolebusted := 0;
  { TotalStems is used for the internal counting of the total number of stems,
    ATotalStems is used to initialize the progress bar of the progress form }
  TotalStems := 0;
  TotalLeaveVertices := 0;
  ViewerTotalLeaves := 0;
  TotalFruit := 0;
  TotalFruitVertices := 0;
  FormatSettings.DecimalSeparator := '.';
  FormatSettings.ThousandSeparator := #0;
  Result := 1;
end;

{ A Stem is a bunch of circles (Sections) formed by points (Vertices)
  stacked as rings to form a 'cylinder'
  that slowly reduces in diameter }
function TFormGlsViewer.AddStem(Sections: TList): Integer;
{ Pre: Sections <> nil
  Ret: 0, AddStem failed
  1, AddStem succeeded, a mesh describing the stem segment made
  up by the sections defined in Sections has been written to 'tree.inc'. }
var
  i, j: Integer; // Loop variables
  Vertices: Integer; // The amount of vertices and
  // this was for POV:
  // faces respectively in the segment mesh
  VerticesPerSection: Integer; // The amount of vertices in a
  // section (is the same for all
  // sections)
  TotalSections: Integer; // Total amount of sections
  Mesh: TGLMeshObject; // Structure containing the actual data
  FaceGroup: TFGVertexIndexList;
  Sign, Variation, Red, Blue, Green: Integer;
begin
  TotalStems := TotalStems + 1;
  { Count the amount of sections }
  TotalSections := Sections.Count;
  { Count the amount of vertices in the stem }
  VerticesPerSection := TList(Sections[0]).Count;
  Vertices := VerticesPerSection * TotalSections;
  { Update the total amount of vertices in the tree }
  TotalVertices := TotalVertices + Vertices;

  Mesh := TGLMeshObject.CreateOwned(TreeFreeForm.MeshObjects);
  // Create the mesh
  Mesh.Mode := momFaceGroups;
  Mesh.Vertices.Clear;
  { Write vertices making up the mesh }
  { For every section making up the stem write the vertices from that section
    to the vertex list of the mesh object }
  for i := 0 to TotalSections - 1 do
  begin

    for j := 0 to VerticesPerSection - 1 do
    begin
      { Repeatedly write a vertex from the current section }
      Mesh.Vertices.add(TPoint3d(TList(Sections[i])[j]).X,
        TPoint3d(TList(Sections[i])[j]).Y, TPoint3d(TList(Sections[i])[j]).z);
      Mesh.Normals.add(TPoint3d(TList(Sections[i])[j]).X,
        TPoint3d(TList(Sections[i])[j]).Y, TPoint3d(TList(Sections[i])[j]).z);
      If FModelParameters.TreeBarkFile = '' then
      begin
        { Mesh.Colors.Add(0,0,1,0); }
        { Calculate the red, green and blue components from the leafcolor variable }
        Red := FModelParameters.TreeColor and $FF;
        Green := (FModelParameters.TreeColor and $FF00) shr 8;
        Blue := (FModelParameters.TreeColor and $FF0000) shr 16;

        if FModelParameters.TreeColorVariation then
        begin
          { Add a small variation to the leaf color }
          if Random(2) = 0 then
            Sign := 1
          else
            Sign := -1;
          Variation := Random(40);
          Red := Red + Sign * Variation;
          Green := Green + Sign * Variation;
          Blue := Blue + Sign * Variation;

          if Red < 0 then
            Red := 0;
          if Green < 0 then
            Green := 0;
          if Blue < 0 then
            Blue := 0;
        end;
        Mesh.Colors.add((Red / $FF), (Green / $FF), (Blue / $FF), 0);
      end;
      // set the tex coords to cover the whole mesh
      with Mesh.TexCoords do
      { This tiles the texture.. Base is still befuddled }
      begin { 0..1 }     { horizontal s, vertical t }  { 8 base cuts 1..10-2 =8 }
        { Add( (j/(VerticesPerSection-1)), (i/TotalSections-1)); }
        If Bolebusted > 7 then
          add((j / (VerticesPerSection - 1)), i)
        else
        begin
          If Bolebusted < 8 then
            add((j / (VerticesPerSection - 1)), (i / 4))
          else
            add((j / (VerticesPerSection - 1)), (i / Bolebusted))
        end;
      end;
    end; { j }
    inc(Bolebusted);
  end; { i }

  // add a new facegroup
  FaceGroup := TFGVertexIndexList.CreateOwned(Mesh.FaceGroups);
  // and set the material
  If FModelParameters.TreeBarkFile <> '' then
    FaceGroup.MaterialName := 'TreeBark';
  { Write the face indices }
  { For every section write the segment mesh. The outer loop has a maximum of
    TotalSections - 2 because the faces are built using points from section i
    and section i + 1 }
  for i := 0 to TotalSections - 2 do
  begin
    for j := 0 to VerticesPerSection - 1 do
    begin
      { Repeatedly write two face indices creating a cubic little plane between
        four vertices   :
        VerticesPerSection * i + j: lower right corner of the first face
        VerticesPerSection * (i + 1) + j: upper right corner of the first face
        VerticesPerSection * i + (j + 1) mod VerticesPerSection: lower left corner of the first face
        VerticesPerSection * i + (j + 1) mod VerticesPerSection: lower left corner of the second face
        VerticesPerSection * (i + 1) + j: upper right corner of the second face
        VerticesPerSection * (i + 1) + (j + 1) mod VerticesPerSection: upper left corner of the second face }
      with FaceGroup do
      begin
        add((VerticesPerSection * i + j));
        add((VerticesPerSection * (i + 1) + j));
        add((VerticesPerSection * i + (j + 1) mod VerticesPerSection));
        add((VerticesPerSection * i + (j + 1) mod VerticesPerSection));
        add((VerticesPerSection * (i + 1) + j));
        add((VerticesPerSection * (i + 1) + (j + 1) mod VerticesPerSection));
      end;
    end;
  end;
  { These facegroups also have a range of rendering modes:
    fgmmTriangles, fgmmTriangleStrip, fgmmFlatTriangles and fgmmTriangleFan. }
  FaceGroup.Mode := fgmmTriangles;
  ProgressBar.StepIt;
  Result := 1;
end;

{ Each Leaf is a closed Polygon...
  use Mesh to place ALL leaves into the Freeform
  The Real LOD advantage would be to make 1 Leaf and Proxies the rest... }
function TFormGlsViewer.AddLeaf(Points: TList; LeafColor: Integer;
  LeafColorVariation: Boolean): Integer;
var
  i: Integer;
  Sign: Integer;
  Red, Blue, Green: Integer;
  Variation: Integer;
  Mesh: TGLMeshObject; // Structure containing the actual data
  FaceGroup: TFGVertexIndexList;
begin
  ViewerTotalLeaves := ViewerTotalLeaves + 1;
  If DisplayWithLeaves then
  begin
    { TreeFreeForm TreeLeafFF }
    Mesh := TGLMeshObject.CreateOwned(LeafFF.MeshObjects); // Create the mesh
    Mesh.Mode := momFaceGroups;
    { (momTriangles, momTriangleStrip, momFaceGroups); }
    Mesh.Vertices.Clear;
    TotalLeaveVertices := TotalLeaveVertices + Points.Count;
    for i := 0 to Points.Count - 1 do
    begin
      Mesh.Vertices.add(TPoint3d(Points[i]).X, TPoint3d(Points[i]).Y,
        TPoint3d(Points[i]).z);
      Mesh.Normals.add(TPoint3d(Points[i]).X, TPoint3d(Points[i]).Y,
        TPoint3d(Points[i]).z);
      If FModelParameters.LeafTextureFile = '' then
      begin
        { Calculate the red, green and blue components from the leafcolor variable }
        { This is broke.. also need to make Front and Bacl Leaf colors }
        Red := LeafColor and $FF;
        Green := (LeafColor and $FF00) shr 8;
        Blue := (LeafColor and $FF0000) shr 16;
        if LeafColorVariation then
        begin
          { Add a small variation to the leaf color }
          if Random(2) = 0 then
            Sign := 1
          else
            Sign := -1;
          Variation := Random(40);
          Red := Red + Sign * Variation;
          Green := Green + Sign * Variation;
          Blue := Blue + Sign * Variation;
          if Red < 0 then
            Red := 0;
          if Green < 0 then
            Green := 0;
          if Blue < 0 then
            Blue := 0;
        end;
        Mesh.Colors.add((Red / $FF), (Green / $FF), (Blue / $FF), 0);
      end;
      { LeafTexture }
      // set the tex coords to cover the whole mesh
      with Mesh.TexCoords do { This does Not tile the texture,corrects offset }
      begin { horizonatl s, vertical t }
        add(LeafPolygonVertices[FModelParameters.LeafShape, i, 0] + 0.5,
          LeafPolygonVertices[FModelParameters.LeafShape, i, 1]);
      end;
    end;
    // add a new facegroup
    FaceGroup := TFGVertexIndexList.CreateOwned(Mesh.FaceGroups);
    // and set the material
    If FModelParameters.LeafTextureFile <> '' then
      FaceGroup.MaterialName := 'LeafTexture';
    { Leaves are clockwise Triangle fans: make triangles }
    for i := 1 to Points.Count - 3 do
      with FaceGroup do
      begin
        add(0);
        add(i);
        add(i + 1);
        add(0);
        add(i + 1);
        add(i + 2);
      end;
    { These facegroups also have a range of rendering modes:
      fgmmTriangles, fgmmTriangleStrip, fgmmFlatTriangles and fgmmTriangleFan. }
    FaceGroup.Mode := fgmmTriangleFan;
    { Winding clockwise (reversed) glFrontFace }
  end;
  ProgressBar.StepIt;
  Result := 1;
end;

{ Each Fruit is a closed Polygon...
  use Mesh to place ALL Fruit into the Freeform
  The Real LOD advantage would be to make 1 Fruit and Proxies the rest... }
function TFormGlsViewer.AddFruit(Points: TList; FruitColor: Integer;
  FruitColorVariation: Boolean): Integer;
var
  i: Integer;
  Sign: Integer;
  Red, Blue, Green: Integer;
  Variation: Integer;
  Mesh: TGLMeshObject; // Structure containing the actual data
  FaceGroup: TFGVertexIndexList;
begin
  TotalFruit := TotalFruit + 1;
  If DisplayWithLeaves then
  begin
    { LeafFF FruitFF }
    Mesh := TGLMeshObject.CreateOwned(FruitFF.MeshObjects); // Create the mesh
    Mesh.Mode := momFaceGroups;
    { (momTriangles, momTriangleStrip, momFaceGroups); }
    Mesh.Vertices.Clear;
    TotalFruitVertices := TotalFruitVertices + Points.Count;
    for i := 0 to Points.Count - 1 do
    begin
      Mesh.Vertices.add(TPoint3d(Points[i]).X, TPoint3d(Points[i]).Y,
        TPoint3d(Points[i]).z);
      Mesh.Normals.add(TPoint3d(Points[i]).X, TPoint3d(Points[i]).Y,
        TPoint3d(Points[i]).z);
      If FModelParameters.FruitTextureFile = '' then
      begin
        { Calculate the red, green and blue components from the leafcolor variable }
        { This is broke.. also need to make Front and Bacl Leaf colors }
        Red := FruitColor and $FF;
        Green := (FruitColor and $FF00) shr 8;
        Blue := (FruitColor and $FF0000) shr 16;
        if FruitColorVariation then
        begin
          { Add a small variation to the leaf color }
          if Random(2) = 0 then
            Sign := 1
          else
            Sign := -1;
          Variation := Random(40);
          Red := Red + Sign * Variation;
          Green := Green + Sign * Variation;
          Blue := Blue + Sign * Variation;
          if Red < 0 then
            Red := 0;
          if Green < 0 then
            Green := 0;
          if Blue < 0 then
            Blue := 0;
        end;
        Mesh.Colors.add((Red / $FF), (Green / $FF), (Blue / $FF), 0);
      end;
      { LeafTexture }
      // set the tex coords to cover the whole mesh
      with Mesh.TexCoords do { This does Not tile the texture,corrects offset }
      begin { horizonatl s, vertical t }
        add(FruitPolygonVertices[i, 0] + 0.5, FruitPolygonVertices[i, 1]);
      end;
    end;
    // add a new facegroup
    FaceGroup := TFGVertexIndexList.CreateOwned(Mesh.FaceGroups);
    // and set the material
    If FModelParameters.FruitTextureFile <> '' then
      FaceGroup.MaterialName := 'FruitTexture';
    { Fruit are clockwise Triangle fans: make triangles }
    for i := 1 to Points.Count - 3 do
      with FaceGroup do
      begin
        add(0);
        add(i);
        add(i + 1);
        add(0);
        add(i + 1);
        add(i + 2);
      end;
    { These facegroups also have a range of rendering modes:
      fgmmTriangles, fgmmTriangleStrip, fgmmFlatTriangles and fgmmTriangleFan. }
    FaceGroup.Mode := fgmmTriangleFan;
    { Winding clockwise (reversed) glFrontFace }
  end;
  ProgressBar.StepIt;
  Result := 1;
end;

end.
